/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.ss7.management.transceiver;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Set;

import javolution.util.FastSet;

/**
 * <p>
 * A multiplex of {@link ShellSelectableChannel} objects.
 * </p>
 * 
 * <p>
 * A selector may be created by invoking the {@link #open open} method of this
 * class, which will use the system's default {@link ChannelProvider
 * </code>selector provider<code>} to create a new selector. A selector remains
 * open until it is closed via its {@link #close close} method.
 * </p>
 * 
 * <p>
 * A selectable channel's registration with a selector is represented by a
 * {@link ChannelSelectionKey} object. A selector maintains one set of selection
 * keys:
 * 
 * <ul>
 * <li>
 * <p>
 * The <i>selected-key set</i> is the set of keys such that each key's channel
 * was detected to be ready for at least one of the operations identified in the
 * key's interest set during a selection operation. This set is returned by the
 * {@link #selectNow() selectNow} method.
 * </p>
 * </li>
 * </ul>
 * </p>
 * 
 * <p>
 * Every time {@link #selectNow() selectNow} method is called the
 * <i>selected-key set</i> is cleared before adding keys that are ready for
 * operation
 * </p>
 * 
 * 
 * @author amit bhayani
 * 
 */
public class ChannelSelector {

	protected Selector selector;

	private FastSet<ChannelSelectionKey> selectedKey = new FastSet<ChannelSelectionKey>();

	protected ChannelSelector(Selector selector) {
		this.selector = selector;
	}

	/**
	 * <p>
	 * Opens a ChannelSelector
	 * </p>
	 * <p>
	 * The new selector is created by invoking the
	 * {@link ChannelProvider#openSelector openSelector} method of the
	 * system-wide default {@link ChannelProvider} object.
	 * </p>
	 * 
	 * @return A new selector
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public static ChannelSelector open() throws IOException {
		return ChannelProvider.provider().openSelector();
	}

	/**
	 * * Selects a set of keys whose corresponding channels are ready for I/O
	 * operations.
	 * 
	 * <p>
	 * This method performs a non-blocking selection operation. If no channels
	 * have become selectable since the previous selection operation then this
	 * method immediately returns empty selected-key set.
	 * </p>
	 * 
	 * @return
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public FastSet<ChannelSelectionKey> selectNow() throws IOException {
		selectedKey.clear();
		selector.selectNow();
		Set<SelectionKey> selection = selector.selectedKeys();
		for (SelectionKey key : selection) {
			if (key.isAcceptable()) {
				ChannelSelectionKey k = (ChannelSelectionKey) key.attachment();
				selectedKey.add(k);
			} else {

				if (key.isReadable()) {
					ChannelSelectionKey k = (ChannelSelectionKey) key.attachment();
					((ShellChannel) k.channel()).doRead();
					if (k.isReadable()) {
						selectedKey.add(k);
					}
				}

				if (key.isWritable()) {
					ChannelSelectionKey k = (ChannelSelectionKey) key.attachment();
					((ShellChannel) k.channel()).doWrite();
					if (k.isWritable()) {
						selectedKey.add(k);
					}
				}
			}
		}//for
		selection.clear();
		return selectedKey;
	}

	/**
	 * Closes this selector.
	 * 
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public void close() throws IOException {
		selector.close();
	}
}
